/*******************************************************************************
   @file    uartStdio.c
   @author  H.M.Shim (elecage@gmail.com)
   @version 
   @date    2013. 8. 12.
   @brief   
  ******************************************************************************
  The MIT License (MIT)

Copyright (c) 2013 <copyright holders>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
  ******************************************************************************
  */



/* Includes ------------------------------------------------------------------*/
#include "stdint.h"
#include "stm32f4_discovery.h"
#include "stm32f4xx_it.h"
#include "stm32f4xx_usart.h"
#include "buffer.h"
#include "uartStdio.h"
#include <stdarg.h>
/* Private typedef -----------------------------------------------------------*/
/**
 * @brief 0 ~ 15 사이의 정수값을 ASCII 캐릭터에 매핑시키기 위한 배열
 */
static const char * const hex = "0123456789abcdef";
/* Private define ------------------------------------------------------------*/
#define UART_BUFFER_LENGTH 	64
/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
BUFFER uart_buf;
/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  UART IO Initialization
  * @param  NONE
  * @retval NONE
  * UART를 이용한 통신 설정을 초기화한다.
  */
void uartInit(void)
{
	 USART_InitTypeDef USART_InitStructure;

	 bufferInit(&uart_buf, UART_BUFFER_LENGTH);
	  /* USARTx configured as follow:
	        - BaudRate = 115200 baud
	        - Word Length = 8 Bits
	        - One Stop Bit
	        - No parity
	        - Hardware flow control disabled (RTS and CTS signals)
	        - Receive and transmit enabled
	  */
	 USART_InitStructure.USART_BaudRate = 115200;
	 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	 USART_InitStructure.USART_StopBits = USART_StopBits_1;
	 USART_InitStructure.USART_Parity = USART_Parity_No;
	 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

	 GPIO_InitTypeDef GPIO_InitStructure;
	 NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)
	  /* Enable GPIO clock */
	 RCC_APB1PeriphClockCmd(KUP_UART_CLK, ENABLE);
	 RCC_AHB1PeriphClockCmd(KPU_UART_GPIO_TX_CLK, ENABLE);
	 RCC_AHB1PeriphClockCmd(KPU_UART_GPIO_RX_CLK, ENABLE);


	  /* Configure USART Tx as alternate function push-pull */
	 GPIO_InitStructure.GPIO_Pin = KPU_UART_TX_PIN;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	 GPIO_Init(KPU_UART_TX_PORT, &GPIO_InitStructure);

	  /* Configure USART Rx as input floating */
	 GPIO_InitStructure.GPIO_Pin = KPU_UART_RX_PIN;
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	 GPIO_Init(KPU_UART_TX_PORT, &GPIO_InitStructure);


	 GPIO_PinAFConfig(KPU_UART_TX_PORT, KPU_UART_TX_SOURCE, GPIO_AF_USART2);
	 GPIO_PinAFConfig(KPU_UART_RX_PORT, KPU_UART_RX_SOURCE, GPIO_AF_USART2);
	  /* USART configuration */
	 USART_Init(KPU_UART, &USART_InitStructure);

	 USART_ITConfig(KPU_UART, USART_IT_RXNE, ENABLE);

	 NVIC_InitStructure.NVIC_IRQChannel = KPU_UART_IRQn;
	 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	 NVIC_Init(&NVIC_InitStructure);
	  /* Enable USART */
	 USART_Cmd(KPU_UART, ENABLE);


}

/**
  * @brief  put a character to UART
  * @param  ch 전송하고자 하는 문자
  * @retval  (0) 캐릭터 전송 성공
  * VCP를 통해 1개의 문자를 전송한다.
  */

int32_t uartPutchar(int8_t ch) {

	USART_SendData(KPU_UART, ch);
	return 0;
}

/**
  * @brief  put a characters array to UART
  * @param  str 전송하고자 하는 문자열
  * @retval 전송한 문자열의 갯수
  * UART를 통해 1개의 문자열을 전송한다. 문자열은 '\0' 문자가 나오기 전까지의 길이만큼 전송한다.
  */
int32_t uartPuts(const int8_t * str)
{
	int32_t ret;
	while(*str !='\0') {
		uartPutchar(*str);
		str++;
		ret++;
	}
	return ret;
}

/**
  * @brief  put a characters array to UART
  * @param  buf 전송하고자 하는 문자열
  * @param  len 전송하고자 하는 문자열의 길이
  * @retval 전송한 문자열의 갯수
  * UART를 통해 길이 len 만큼의 문자열을 전송한다.
  */
int32_t uartWrite(const int8_t *buf, uint32_t len)
{
	uint32_t idx;

	for(idx = 0 ; idx < len ; idx++) {
		if(buf[idx] =='\n') {
			uartPutchar('\r');
		}
		uartPutchar(buf[idx]);
	}

	return idx;
}


/**
  * @brief  printf implementation for  UART
  * @param  str 전송하고자 하는 문자열
  * @param  ... printf문의 선택 인자
  * @retval NONE
  * UART를 이용한 printf문의 구현
  */
void uartPrintf(const int8_t *str, ...)
{
	uint32_t idx;								/**< 반복문 인덱스 */
	uint32_t value;								/**< 정수형 arg 값을 받아들이기 위한 변수 */
	uint32_t pos;
	uint32_t count;
	uint32_t base;								/**< 진법. 10진수, 16진수 */
	uint32_t neg;								/**< 입력받은 정수가 음수인지 양수인지 표시 */
	int8_t *s;									/**< 문자열 변수 */
	int8_t buf[16];
	int8_t fill;								/**< 출력시 채워넣는 값 */
	va_list arg;								/**< 가변인자 변수 */


	va_start(arg, str);							/**< 가변인자 처리 시작 */

	while(*str != '\0') {
		/**
		 * 첫번째로 '%' 캐릭터나 문자열 종료가 아닌 캐릭터를 찾는다.
		 */
		for(idx = 0 ; (str[idx] != '%') && (str[idx] != '\0') ; idx++) ;

		uartWrite(str, idx);						/**< 시작점부터 그 위치까지 출력한다. */

		str += idx;

		/**
		 * % 문자열 처리를 수행한다.
		 */
		if(*str == '%') {
			str++;

			count = 0;
			fill = ' ';

again:
			switch(*str++) {
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				/**
				 * 만일 맨 앞자리 숫자가 0이면 캐릭터 '0'을 채워넣는다.
				 */
				if( (str[-1] =='0') && (count == 0) ) {
					fill = '0';
				}

				/**
				 * 자리수를 갱신한다.
				 */
				count *= 10;
				count += str[-1] - '0';
				goto again;

			case 'c':
				value = va_arg(arg, uint32_t);	/**< arg의 값을 가져온다. */
				/**
				 * 캐릭터를 출력한다.
				 */
				uartPutchar((uint8_t)value);
				break;

			case 'd':
			case 'i':							/**< %d와 %i 처리 - 정수 */
				value = va_arg(arg, uint32_t);	/**< arg의 값을 가져온다. */
				/**
				 * 버퍼의 위치를 리셋한다.
				 */
				pos = 0;
				/**
				 * 만일 입력값이 음수이면 숫자는 양수로 바꾸어주고
				 * '-' 부호를 붙여준다.
				 */
				if(value < 0) {
					value = -value;
					/**
					 * 음수임을 표시한다.
					 * neg == 0 : 양수, neg == 1 음수
					 */
					neg = 1;
				} else {
					neg = 0;
				}
				base = 10; 						/**< 베이스를 10으로 설정한다. */

				goto convert;					/**< ASCII로 변환하는 곳으로 이동한다. */


			case 's':			/**<%s 처리 - 문자열 */
				s = va_arg(arg, int8_t *);	/**< arg의 값을 가져온다. */

				uartPuts(s);

				/**
				 * 요구하는 만큼의 추가 공간을 출력한다.
				 */
				if(count > idx) {
					count -= idx;
					while(count--) {
						uartWrite((int8_t*)" ", 1);
					}
				}
				break;

			case 'u':		/**< %u 처리. 양의 정수 */
				value = va_arg(arg, uint32_t);	/**< arg의 값을 가져온다. */
				pos = 0;						/**< 버퍼의 위치를 리셋한다. */
				base = 10;						/**< 베이스를 10으로 설정한다. */
				neg = 0;						/**< 항상 양수이기 때문에 0으로 설정한다. */
				goto convert;					/**< ASCII로 변환하는 곳으로 이동한다. */

			case 'x':
			case 'X':
			case 'p':		/**< %x 처리. 16진수 출력 */
				value = va_arg(arg, uint32_t);	/**< arg의 값을 가져온다. */
				pos = 0;						/**< 버퍼의 위치를 리셋한다. */
				base = 16;						/**< 베이스를 16으로 설정한다. */
				neg = 0;						/**< 항상 양수이기 때문에 0으로 설정한다. */

convert:
				for(idx = 1 ;
					(((idx * base) <= value) &&
					(((idx * base)/base) == idx));
					idx *= base, count--);

				if(neg) {
					count--;
				}

				if(neg && (fill == '0')) {
					buf[pos++] = '-';
					neg = 0;
				}

				if((count > 1) &&(count < 16)) {
					for(count--; count; count--) {
						buf[pos++] = fill;
					}
				}

				/**
				 * 만일 value가 dmatndlaus '-' 부호를 숫자 앞에 붙인다.
				 */
				if(neg) {
					buf[pos++] = '-';
				}

				/**
				 * value를 문자열로 변환한다.
				 */
				for(;idx ; idx /= base) {
					buf[pos++] = hex[(value / idx) % base];

				}

				uartWrite(buf, pos);			/**< 문자열을 출력한다. */
				break;
			case '%':		/**< %% 처리 */
				uartPutchar('%');
				break;
			default:
				uartPuts((int8_t*)"ERROR");
				break;
			}
		}
	}

	va_end(arg);
}

/**
  * @brief  get a character from UART
  * @param  NONE
  * @retval UART로부터 획득한 문자
  * UART를 통해 길이 1개의 문자를 수신한다.
  */
int8_t uartGetchar(void)
{
	int8_t ret = 0;
	while(bufferRead(&uart_buf, &ret) != 0);
	return ret;

}

/**
  * @brief  get a character string from UART
  * @param  buf UART를 통해 수신한 문자를 저장할 배열의 포인터
  * @param  len UART로부터 수신하고자 하는 최대 문자의 갯수
  * @retval 실제로 수신한 문자열의 갯수
  * UART를 통해 문자열을 수신한다.
  * '\r' 또는 '\n'이 수신될 때 까지 데이터를 수신한다.
  * 백스페이스 문자는 자신과 이전 문자를 삭제하는 기능을 수행한다.
  */
int32_t uartGets(int8_t *buf, int32_t len)
{
	uint32_t count = 0;
	int8_t ch;
	static int8_t lastWasCR = 0;

	len--;				/**< NULL 종료문자를 제외하기 위해 len을 1만큼 줄인다. */

	/**
	 * CR 또는 LF가 입력될 때까지 진행한다.
	 */
	while(1) {
		ch = uartGetchar();	// 한개의 문자를 입력받는다.

		if(ch == '\b')
		{
			if(count) {
				uartWrite((int8_t*)"\b \b", 3);
				count--;
			}
			// 다음 문자열을 읽는 것은 생략한다.
			continue;
		}

		/**
		 * 만약 문자가 LF이고 이전 문자가 CR이면
		 */
		if((ch == '\n') && lastWasCR) {
			lastWasCR = 0;
			continue;
		}

		if((ch == '\r') || (ch == '\n') || (ch == 0x1b)) {
			if(ch == '\r') {
				lastWasCR = 1;
			}
			/**
			 * 입력 처리를 멈춘다.
			 */
			break;
		}

		if(count < len) {
			buf[count] = ch;
			count++;
			uartPutchar(ch);		/**< echo character */
		}
	}

	buf[count] = 0;				/**< 문자열 종료 표시 */
	uartPuts((int8_t*)"\r\n");

	return count;
}

